#pragma once

#include "PillboxCollider.h"
#include "Obstacle.h"

#include "RobotController.h"

class SimManager
{
	PillboxCollider m_Collider;
	Obstacle m_arrObstacles[8];
	int m_nNumObstacles;
	Pos2d m_p2Goal;
	double m_lfGoalRad;

	RobotController m_Controller;


	int m_nNumSides; 
	Pos2d m_p2ObstacleMin, m_p2ObstacleMax;
	double m_lfObstacleRad;

	int m_nHitGoalCount, m_nHitObstacleCount, m_nMissedGoalCount;

	double m_lfCurrGoodness;

	bool m_bBoostWithQlearn, m_bQlearnOnly;

	double m_lfAnnealMagic;
public:
	SimManager(void);
	~SimManager(void);

	inline void SetMagicAnneal(double lfMagic) { m_lfAnnealMagic = lfMagic; }
	inline double GetMagicAnneal() const { return m_lfAnnealMagic; }

	const PillboxCollider & AccessCollider() { return m_Collider; }

	const Obstacle & AccessObstacle(int nWhichObstacle) const { return m_arrObstacles[nWhichObstacle]; }

	int GetNumObstacles() const { return m_nNumObstacles; }

	bool MoveRobot(int nTurnAmount, int nSpeed); // returns whether we collided

	inline double GetMostRecentGoodness() const { return m_lfCurrGoodness; }

	void RunRangeFinders();

	void SetBoostWithQlearn(bool bQlearn) { m_bBoostWithQlearn = bQlearn; }

	void SetUseQlearnOnly(bool bQlearn) { m_bQlearnOnly = bQlearn; }

	void SetRobotParams(double lfLength, double lfWidth, double lfXPos, double lfYPos, double lfTheta, double lfAngleStep);

	inline void SetRobotAngleStep(double lfAngleStep)
	{
		m_Collider.SetAngleStep(lfAngleStep);
	}

	inline void SetGoal(double lfX, double lfY, double lfRad)
	{
		m_p2Goal[0] = lfX; m_p2Goal[1] = lfY; m_lfGoalRad = lfRad;
	}

	inline double GetGoalX() const { return m_p2Goal[0]; }
	inline double GetGoalY() const { return m_p2Goal[1]; }
	inline double GetGoalRad() const { return m_lfGoalRad; }

	inline void SetNumObstacles(int nNumObstacles)
	{
		m_nNumObstacles = nNumObstacles;
	}

	bool SetupObstacle(int iWhichObstacle, Pos2d p2ObstacleMin, Pos2d p2ObstacleMax, double lfObstacleRadius);

	bool SetObstacleNumPolys(int nNumPts);

	void SetAllObstacleParams(int nNumObstacles, int nNumSides, Pos2d p2ObstacleMin, Pos2d p2ObstacleMax, double lfObstacleRad);

	void TrainOnce(double lfActionSelectionBias, double lfMaxSelectionBias, double lfCollisionPunishment,
		double lfTrainRate, double lfDiscountFactor);

	void EvalOnce(FILE * fiOut);

	void Train(int nTimes, const char * szFileOut, int nEvalFreq, double lfLearningRate, double lfDiscountFactor);

	void PlaceRobotAndGoal();
		
	void SetTrainingObstacleParams(int nNumObstacles, int nNumSides, Pos2d p2ObstacleMin, Pos2d p2ObstacleMax, double lfObstacleRad);

	bool RunRobotOneStep(bool & bHitGoal);

	void LoadNeuralNet(const char * szFiIn)
	{
		m_Controller.LoadNeuralNet(szFiIn);
	}

	void SaveNeuralNet(const char * szFiOut)
	{
		m_Controller.SaveNeuralNet(szFiOut);
	}

	
	inline int NumGoalHits() const { return m_nHitGoalCount; }
	inline int NumObstacleCollisions() const { return m_nHitObstacleCount; }
	inline int NumGoalMisses() const { return m_nMissedGoalCount; }
};
